using System.Text;
using System.Collections.Generic;
using System.Linq;
using PingPong.GameEngine;
using PingPong.GameEngine.Components;
using PingPong.Generic;
using PingPong.Generic.Helpers;



using System;

namespace PingPong.GameEngine.Parser {



public class Parser {
	public const int _EOF = 0;
	public const int _identifier = 1;
	public const int _string = 2;
	public const int _equals = 3;
	public const int _leftParen = 4;
	public const int _rightParen = 5;
	public const int _leftBrace = 6;
	public const int _rightBrace = 7;
	public const int _lineFeed = 8;
	public const int maxT = 20;

	const bool T = true;
	const bool x = false;
	const int minErrDist = 2;
	
	public Scanner scanner;
	public Errors  errors;

	public Token t;    // last recognized token
	public Token la;   // lookahead token
	int errDist = minErrDist;

public GameDefinition GameDefinition { get; set; }

/* Syntax definitions */



	public Parser(Scanner scanner) {
		this.scanner = scanner;
		errors = new Errors();
	}

	void SynErr (int n) {
		if (errDist >= minErrDist) errors.SynErr(la.line, la.col, n, la);
		errDist = 0;
	}

	public void SemErr (string msg) {
		if (errDist >= minErrDist) errors.SemErr(t.line, t.col, msg, t);
		errDist = 0;
	}
	
	void Get () {
		for (;;) {
			t = la;
			la = scanner.Scan();
			if (la.kind <= maxT) { ++errDist; break; }

			la = t;
		}
	}
	
	void Expect (int n) {
		if (la.kind==n) Get(); else { SynErr(n); }
	}
	
	bool StartOf (int s) {
		return set[s, la.kind];
	}
	
	void ExpectWeak (int n, int follow) {
		if (la.kind == n) Get();
		else {
			SynErr(n);
			while (!StartOf(follow)) Get();
		}
	}


	bool WeakSeparator(int n, int syFol, int repFol) {
		int kind = la.kind;
		if (kind == n) {Get(); return true;}
		else if (StartOf(repFol)) {return false;}
		else {
			SynErr(n);
			while (!(set[syFol, kind] || set[repFol, kind] || set[0, kind])) {
				Get();
				kind = la.kind;
			}
			return StartOf(syFol);
		}
	}

	
	void GameDefinitionSyntax() {
		this.GameDefinition = new GameDefinition(); 
		Expect(9);
		Expect(1);
		this.GameDefinition.SetGameDefinitionName(t.val); 
		Expect(10);
		while (StartOf(1)) {
			if (la.kind == 11) {
				TeamRotationTypeDeclaration();
			} else if (la.kind == 12) {
				TeamCountDeclaration();
			} else if (la.kind == 13) {
				TeamSizeDeclaration();
			} else if (la.kind == 14) {
				ScoringTypeDeclaration();
			} else {
				GameEventDeclaration();
			}
		}
	}

	void TeamRotationTypeDeclaration() {
		Expect(11);
		Expect(4);
		Expect(1);
		this.GameDefinition.SetTeamRotationType(EnumHelper.GetEnumValue<TeamRotationType>(t.val)); 
		Expect(5);
	}

	void TeamCountDeclaration() {
		string expression = string.Empty; 
		Expect(12);
		Expect(4);
		AnyExpression(out expression);
		this.GameDefinition.SetTeamCount(expression); 
		Expect(5);
	}

	void TeamSizeDeclaration() {
		string expression = string.Empty; 
		Expect(13);
		Expect(4);
		AnyExpression(out expression);
		this.GameDefinition.SetTeamSize(expression); 
		Expect(5);
	}

	void ScoringTypeDeclaration() {
		Expect(14);
		Expect(4);
		Expect(1);
		this.GameDefinition.SetScoringType(EnumHelper.GetEnumValue<ScoringType>(t.val)); 
		Expect(5);
	}

	void GameEventDeclaration() {
		string expression = string.Empty;
		GameEvent gameEvent = new GameEvent();
		this.GameDefinition.AddGameEvent(gameEvent);
		
		Expect(15);
		AnyExpression(out expression);
		gameEvent.AddGameCondition(new GameCondition().SetExpression(expression)); 
		while (la.kind == 16 || la.kind == 17) {
			if (la.kind == 16) {
				Get();
				AnyExpression(out expression);
				gameEvent.AddGameCondition(new GameCondition().SetExpression(expression, ExpressionBooleanType.Or)); 
			} else {
				Get();
				AnyExpression(out expression);
				gameEvent.AddGameCondition(new GameCondition().SetExpression(expression, ExpressionBooleanType.And)); 
			}
		}
		Expect(18);
		GameActionDeclaration(ref gameEvent);
		while (la.kind == 18) {
			Get();
			GameActionDeclaration(ref gameEvent);
		}
	}

	void AnyExpression(out string expression) {
		expression = string.Empty;
		StringBuilder builder = new StringBuilder();
		
		Expect(6);
		Get();
		builder.Append(t.val); 
		while (StartOf(2)) {
			Get();
			builder.Append(t.val); 
		}
		Expect(7);
		expression = builder.ToString(); 
	}

	void GameActionDeclaration(ref GameEvent gameEvent) {
		string expression = string.Empty; 
		GameAction gameAction = new GameAction(); 
		
		Expect(19);
		Expect(1);
		gameAction.SetGameActionType(EnumHelper.GetEnumValue<GameActionType>(t.val)); 
		if (la.kind == 6) {
			AnyExpression(out expression);
			gameAction.SetScalarExpression(expression); 
		}
		gameEvent.AddGameAction(gameAction); 
	}



	public void Parse() {
		la = new Token();
		la.val = "";		
		Get();
		GameDefinitionSyntax();
		Expect(0);

	}
	
	static readonly bool[,] set = {
		{T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x},
		{x,x,x,x, x,x,x,x, x,x,x,T, T,T,T,T, x,x,x,x, x,x},
		{x,T,T,T, T,T,T,x, T,T,T,T, T,T,T,T, T,T,T,T, T,x}

	};
} // end Parser


public class Errors {
	public int count = 0;                                    // number of errors detected
	public System.Collections.Generic.List<string> errors = new System.Collections.Generic.List<string>();
	public System.Collections.Generic.List<PingPong.GameEngine.Parser.Error> errorList = new System.Collections.Generic.List<PingPong.GameEngine.Parser.Error>();
	public string errMsgFormat = "-- line {0} col {1}: {2}"; // 0=line, 1=column, 2=text

	public virtual void SynErr (int line, int col, int n, Token t) {
		string s;
		switch (n) {
			case 0: s = "EOF expected"; break;
			case 1: s = "identifier expected"; break;
			case 2: s = "string expected"; break;
			case 3: s = "equals expected"; break;
			case 4: s = "leftParen expected"; break;
			case 5: s = "rightParen expected"; break;
			case 6: s = "leftBrace expected"; break;
			case 7: s = "rightBrace expected"; break;
			case 8: s = "lineFeed expected"; break;
			case 9: s = "\"define\" expected"; break;
			case 10: s = "\"as\" expected"; break;
			case 11: s = "\"RotationType\" expected"; break;
			case 12: s = "\"TeamCount\" expected"; break;
			case 13: s = "\"TeamSize\" expected"; break;
			case 14: s = "\"ScoringType\" expected"; break;
			case 15: s = "\"when\" expected"; break;
			case 16: s = "\"or\" expected"; break;
			case 17: s = "\"and\" expected"; break;
			case 18: s = "\"then\" expected"; break;
			case 19: s = "\"call\" expected"; break;
			case 20: s = "??? expected"; break;

			default: s = "error " + n; break;
		}
		errors.Add(string.Format(errMsgFormat, line, col, s));
		errorList.Add(new PingPong.GameEngine.Parser.Error(line, col, s, t));
		count++;
	}

	public virtual void SemErr (int line, int col, string s, Token t) {
		errors.Add(string.Format(errMsgFormat, line, col, s));
		errorList.Add(new PingPong.GameEngine.Parser.Error(line, col, s, t));
		count++;
	}
	
	public virtual void SemErr (string s, Token t) {
		errors.Add(s);
		errorList.Add(new PingPong.GameEngine.Parser.Error(s, t));
		count++;
	}
	
	public virtual void Warning (int line, int col, string s, Token t) {
		errors.Add(string.Format(errMsgFormat, line, col, s));
		errorList.Add(new PingPong.GameEngine.Parser.Error(line, col, s, t));
	}
	
	public virtual void Warning(string s, Token t) {
		errors.Add(s);
		errorList.Add(new PingPong.GameEngine.Parser.Error(s, t));
	}
} // Errors


public class FatalError: Exception {
	public FatalError(string m): base(m) {}
}
}